﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.IO.Compression;
using System.IO;

namespace Marbles.Core.Filters
{
    public class CompressedPageStatePersister : PageStatePersister
    {
        protected new LosFormatter StateFormatter
        {
            get
            {
                if (_stateFormatter == null) { _stateFormatter = new LosFormatter(); }
                return _stateFormatter;
            }
        }

        private LosFormatter _stateFormatter;
        private CompressionMethod _CompressionMethod;

        public CompressedPageStatePersister(System.Web.UI.Page page, CompressionMethod CompressionMethod)
            : base(page)
        {
            _CompressionMethod = CompressionMethod;
        }

        public override void Load()
        {
            Guid id = Guid.Empty;
            Byte[] result = null;

            if (base.Page.Request.Form["__VSKEY"] != null) { id = new Guid(base.Page.Request.Form["__VSKEY"].ToString()); }

            Byte[] bytes = Convert.FromBase64String(base.Page.Request.Form["__VSTATE"]);

            switch (_CompressionMethod)
            {
                case CompressionMethod.Deflate:
                    result = DeflateDecompress(bytes);
                    break;
                case CompressionMethod.GZip:
                    result = GZipDecompress(bytes);
                    break;
            }

            try
            {
                Pair p = StateFormatter.Deserialize(Convert.ToBase64String(result)) as Pair;
                base.ViewState = p.First;
                base.ControlState = p.Second;
            }
            catch { }
        }

        public override void Save()
        {
            Guid id = Guid.NewGuid();
            System.Web.UI.ScriptManager sm = System.Web.UI.ScriptManager.GetCurrent(this.Page);

            StringWriter writer = new StringWriter();
            StateFormatter.Serialize(writer, new Pair(base.ViewState, base.ControlState));
            Byte[] bytes = Convert.FromBase64String(writer.ToString());
            Byte[] result = null;

            switch (_CompressionMethod)
            {
                case CompressionMethod.Deflate:
                    result = DeflateCompress(bytes);
                    break;
                case CompressionMethod.GZip:
                    result = GzipCompress(bytes);
                    break;
            }

            if (sm != null && sm.IsInAsyncPostBack)
            {
                System.Web.UI.ScriptManager.RegisterHiddenField(this.Page, "__VSTATE", Convert.ToBase64String(result));
                System.Web.UI.ScriptManager.RegisterHiddenField(this.Page, "__VSKEY", id.ToString());
            }
            else
            {
                base.Page.ClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(result));
                base.Page.ClientScript.RegisterHiddenField("__VSKEY", id.ToString());
            }
        }

        private byte[] GzipCompress(byte[] uncompData)
        {
            using (MemoryStream mem = new MemoryStream())
            {
                using (GZipStream gzip = new GZipStream(mem, CompressionMode.Compress, true))
                {
                    gzip.Write(uncompData, 0, uncompData.Length);
                }
                return mem.ToArray();
            }
        }

        private byte[] GZipDecompress(byte[] compData)
        {
            GZipStream gzip;
            using (MemoryStream inputMem = new MemoryStream())
            {
                inputMem.Write(compData, 0, compData.Length);
                inputMem.Position = 0;
                gzip = new GZipStream(inputMem, CompressionMode.Decompress, true);

                using (MemoryStream outputMem = new MemoryStream())
                {
                    byte[] buf = new byte[1024];
                    int byteRead = -1;
                    byteRead = gzip.Read(buf, 0, buf.Length);
                    while (byteRead > 0)
                    {
                        outputMem.Write(buf, 0, byteRead);
                        byteRead = gzip.Read(buf, 0, buf.Length);
                    }
                    gzip.Close();
                    return outputMem.ToArray();
                }
            }
        }

        private byte[] DeflateCompress(byte[] uncompData)
        {
            using (MemoryStream mem = new MemoryStream())
            {
                using (DeflateStream Deflate = new DeflateStream(mem, CompressionMode.Compress, true))
                {
                    Deflate.Write(uncompData, 0, uncompData.Length);
                }
                return mem.ToArray();
            }
        }

        private byte[] DeflateDecompress(byte[] compData)
        {
            DeflateStream Deflate;
            using (MemoryStream inputMem = new MemoryStream())
            {
                inputMem.Write(compData, 0, compData.Length);
                inputMem.Position = 0;
                Deflate = new DeflateStream(inputMem, CompressionMode.Decompress, true);

                using (MemoryStream outputMem = new MemoryStream())
                {
                    byte[] buf = new byte[1024];
                    int byteRead = -1;
                    byteRead = Deflate.Read(buf, 0, buf.Length);
                    while (byteRead > 0)
                    {
                        outputMem.Write(buf, 0, byteRead);
                        byteRead = Deflate.Read(buf, 0, buf.Length);
                    }
                    Deflate.Close();
                    return outputMem.ToArray();
                }
            }
        }
    }

    public enum CompressionMethod { GZip, Deflate }
}
